package com.hjk.common.utils.date;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.hjk.common.utils.check.Check;
import org.junit.Test;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * java 1.8 localDateTime工具类
 *
 * @author huangjunkai
 * @date 2020年11月20日11:00:34
 */
public class MyDateTimeUtils {

    /**
     * 一分钟毫秒值
     */
    public static long MINUTE_TIME = 1000 * 60;
    /**
     * 一个小时的毫秒值
     */
    public static long HOUR_TIME = 1000 * 60 * 60;

    /**
     * 一天毫秒值
     */
    public static long DAY_TIME = 1000 * 60 * 60 * 24;

    /**
     * 一天 秒值
     */
    public static long DAY_TIME_SECONDS = 60 * 60 * 24;


    /**
     * 标准日期格式：yyyy-MM-dd
     **/
    public static String DATE_PATTERN = "yyyy-MM-dd";


    /**
     * 标准时间格式：HH:mm:ss
     */
    public static String TIME_PATTERN = "HH:mm:ss";


    /**
     * 标准日期时间格式，精确到分：yyyy-MM-dd HH:mm
     */
    public static String DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm";


    /**
     * 标准日期时间格式，精确到秒：yyyy-MM-dd HH:mm:ss
     */
    public static String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 标准日期格式：yyyyMMddHHmm 到小时
     */
    public static String PURE_DATETIME_H_PATTERN = "yyyyMMddHH";

    /**
     * 标准日期时间格式，精确到毫秒：yyyy-MM-dd HH:mm:ss.SSS
     */
    public static String DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * 标准日期时间格式，精确到毫秒：yyyy/MM/dd HH:mm:ss.SSS
     */
    public static String DATETIME_SWAY_PATTERN = "yyyy/MM/dd HH:mm:ss.SSS";
    /**
     * 标准日期格式：yyyy年MM月dd日
     */
    public static String CHINESE_DATE_PATTERN = "yyyy年MM月dd日";

    /**
     * 标准日期格式：yyyy年MM月dd日
     */
    public static String CHINESE_DATE_TIME_PATTERN = "yyyy年MM月dd日 HH时mm分ss秒";

    /**
     * 标准日期格式：yyyyMMdd
     */
    public static String PURE_DATE_PATTERN = "yyyyMMdd";


    /**
     * 标准日期格式：HHmmss
     */
    public static String PURE_TIME_PATTERN = "HHmmss";

    /**
     * 标准日期格式：yyyyMMddHHmm 到分钟
     */
    public static String PURE_DATETIME_M_PATTERN = "yyyyMMddHHmm";

    /**
     * 标准日期格式：yyyyMMddHHmmss  到秒
     */
    public static String PURE_DATETIME_PATTERN = "yyyyMMddHHmmss";


    /**
     * 标准日期格式：yyyyMMddHHmmssSSS 到毫秒
     */
    public static String PURE_DATETIME_MS_PATTERN = "yyyyMMddHHmmssSSS";

    //-------------------------------------------------------------------------------------------------------------------------------- Others
    /**
     * HTTP头中日期时间格式：EEE, dd MMM yyyy HH:mm:ss z
     */
    public static String HTTP_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";

    /**
     * JDK中日期时间格式：EEE MMM dd HH:mm:ss zzz yyyy
     */
    public static String JDK_DATETIME_PATTERN = "EEE MMM dd HH:mm:ss zzz yyyy";


    private static final ZoneId zone = ZoneId.systemDefault();
    private static final ZoneOffset zoneOffset = ZoneOffset.UTC;

    /**
     * 日期格式化 缓存
     */
    private static final Map<String, DateTimeFormatter> FORMATTERS = new ConcurrentHashMap<>();

    /**
     * 获取当前时间
     *
     * @return
     */
    public static LocalDateTime now() {
        return LocalDateTime.now();
    }

    /**
     * 获取 yearMonth yyyy-MM
     *
     * @return
     */
    public static String getYearMonth() {
        return YearMonth.now().toString();
    }

    /**
     * 获取 localDate yyyy-MM-dd
     *
     * @return
     */
    public static String getLocalDate() {
        return LocalDate.now().toString();
    }


    /**
     * 昨天
     *
     * @return
     */
    public static LocalDateTime yesterday() {
        return now().plusDays(-1);
    }

    /**
     * 明天
     *
     * @return
     */
    public static LocalDateTime tomorrow() {
        return now().plusDays(1);
    }

    /**
     * 将 LocalDateTime 转成  Date
     *
     * @return Date
     * @see LocalDateTime  =>
     * @see Date
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(zone).toInstant());
    }

    /**
     * 将  Date 转成  LocalDateTime
     *
     * @return LocalDateTime
     * @see Date  =>
     * @see LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return date.toInstant().atZone(zone).toLocalDateTime();
    }

    /**
     * 将  Date 转成  LocalDate
     *
     * @return LocalDateTime
     * @see Date  =>
     * @see LocalDate
     */
    public static LocalDate toLocalDate(Date date) {
        return date.toInstant().atZone(zone).toLocalDate();
    }


    //第二部分：解析  日期格式化   字符串日期 格式化成 LocalDateTime

    /**
     * 解析 "yyyy-MM-dd HH:mm:ss" 格式字符串 成 LocalDateTime
     *
     * @param text 日期字符串
     * @return LocalDateTime
     */
    public static LocalDateTime parseDefault(String text) {
        return LocalDateTime.parse(text, MyDateTimeUtils.findFormatter(DATETIME_PATTERN));
    }

    /**
     * 解析 日期字符串  成 LocalDateTime
     *
     * @param text    日期字符串
     * @param pattern 格式
     * @return
     */
    public static LocalDateTime parseDateTime(String text, String pattern) {
        return LocalDateTime.parse(text, MyDateTimeUtils.findFormatter(pattern));
    }

    /**
     * 返回 00:00日期
     *
     * @param text
     * @return
     */
    public static LocalDateTime parseDate(String text) {
        return LocalDate.parse(text).atStartOfDay();
    }

    /**
     * 解析字符串为
     *
     * @param text    日期字符串
     * @param pattern 格式
     * @return
     */
    public static LocalDateTime parseDate(String text, String pattern) {
        return LocalDate.parse(text, findFormatter(pattern)).atStartOfDay();
    }

    public static LocalDateTime parseTime(String text) {
        return LocalDate.now().atTime(LocalTime.parse(text));
    }

    public static LocalDateTime parseTime(String text, String pattern) {
        return LocalDate.now().atTime(LocalTime.parse(text, findFormatter(pattern)));
    }

    //第三部分：格式化

    /**
     * 返回 当前日期   格式  yyyy-MM-dd HH:mm:ss
     *
     * @return 当前日期字符串
     */
    public static String formatDefaultDate() {
        return now().format(findFormatter(DATETIME_PATTERN));
    }


    /**
     * LocalDateTime日期默认 解析 成 "yyyy-MM-dd HH:mm:ss" 格式字符串
     *
     * @param dt LocalDateTime
     * @return "yyyy-MM-dd HH:mm:ss" 格式字符串
     */
    public static String formatDefault(LocalDateTime dt) {
        return dt.format(findFormatter(DATETIME_PATTERN));
    }

    /**
     * LocalDateTime日期默认 解析 成 "yyyy-MM-dd HH:mm:ss" 格式字符串
     *
     * @param date Date
     * @return "yyyy-MM-dd HH:mm:ss" 格式字符串
     */
    public static String formatDefault(Date date) {
        return toLocalDateTime(date).format(findFormatter(DATETIME_PATTERN));
    }

    /**
     * 返回 当前日期字符串 自定义格式 LocalDate
     *
     * @param pattern
     * @return
     */
    public static String formatDate(String pattern) {
        return now().toLocalDate().format(findFormatter(pattern));
    }

    @Test
    public void test3() {
        String dateTime = formatDateTime(DATETIME_SWAY_PATTERN);
        System.out.println(dateTime.substring(0, dateTime.length() - 2));

    }

    /**
     * 返回 当前日期 字符串
     *
     * @return
     */
    public static String formatDateTime() {
        return now().toString();
    }

    /**
     * 返回 当前日期字符串  LocalDateTime
     *
     * @param pattern 自定义格式
     * @return 当前日期字符串
     */
    public static String formatDateTime(String pattern) {
        return now().format(findFormatter(pattern));
    }

    public static String formatTime() {
        return now().toLocalTime().toString();
    }

    public static String formatTime(String pattern) {
        DateTimeFormatter format = findFormatter(pattern);
        return now().toLocalTime().format(format);
    }

    //第四部分：转换成java日期
    public LocalDate toLocalDate() {
        return LocalDate.now();
    }

    public LocalTime toLocalTime() {
        return LocalTime.now();
    }

    public Instant toInstant() {
        return Instant.now();
    }

    public Instant toInstant(LocalDateTime dateTime) {
        return dateTime.toInstant(zoneOffset);
    }


    //第五部分：计算

    /**
     * 当前日期 增加天数
     *
     * @param days 天数
     * @return LocalDateTime格式
     */
    public LocalDateTime plusDays(int days) {
        return now().plusDays(days);
    }

    /**
     * 日期 增加天数
     *
     * @param date 日期
     * @param days 增加天数
     * @return date
     */
    public Date plusDays(Date date, int days) {
        return toDate(toLocalDateTime(date).plusDays(days));
    }

    public LocalDateTime plusYears(int years) {
        return now().plusYears(years);
    }

    public LocalDateTime plusWeeks(int weeks) {
        return now().plusWeeks(weeks);
    }

    public LocalDateTime plusMonths(int months) {
        return now().plusMonths(months);
    }

    public LocalDateTime plusHours(int hours) {
        return now().plusHours(hours);
    }

    public LocalDateTime plusMinutes(int minutes) {
        return now().plusMinutes(minutes);
    }

    public LocalDateTime plusSeconds(int seconds) {
        return now().plusSeconds(seconds);
    }

    public LocalDateTime plusNanos(int nanos) {
        return now().plusNanos(nanos);
    }

    //===================================第六部分：调整值===================================

    /**
     * 获取当月的最后一天
     */
    public static Date getLastMonthDay() {
        return Date.from(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取传入日期的最后一天
     */
    public static Date getLastMonthDay(Date date) {
        return Date.from(toLocalDate(date).with(TemporalAdjusters.lastDayOfMonth()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取当月的最后一天
     */
    public static String getLastMonthDayString() {
        return formatDefault(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).atStartOfDay());
    }

    /**
     * 获取传入日期的最后一天
     */
    public static String getLastMonthDayString(Date date) {
        return formatDefault(toLocalDate(date).with(TemporalAdjusters.lastDayOfMonth()).atStartOfDay());
    }

    /**
     * 获取当月的第一后一天
     */
    public static Date getFirstMonthDay() {
        return Date.from(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取当月的最后一天
     */
    public static Date getFirstMonthDay(Date date) {
        return Date.from(toLocalDate(date).with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    @Test
    public void test334() {

        System.out.println(formatDateTime());

    }

    /**
     * 当前日期的 上个星期一
     *
     * @return
     */
    public LocalDateTime previousOrSameMonday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    }

    /**
     * 当前日期的 上个星期二
     *
     * @return
     */
    public LocalDateTime previousOrSameTuesday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY));
    }

    /**
     * 当前日期的 上个星期三
     *
     * @return
     */
    public LocalDateTime previousOrSameWendesday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.WEDNESDAY));
    }

    /**
     * 当前日期的 上个星期四
     *
     * @return
     */
    public LocalDateTime previousOrSameThursday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.THURSDAY));
    }

    /**
     * 当前日期的 上个星期五
     *
     * @return
     */
    public LocalDateTime previousOrSameFriday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));
    }

    /**
     * 当前日期的 上个星期六
     *
     * @return
     */
    public LocalDateTime previousOrSameSaturday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.SATURDAY));
    }

    /**
     * 当前日期的 上个星期日
     *
     * @return
     */
    public LocalDateTime previousOrSameSunday() {
        return now().with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
    }

    /**
     * 当前日期的 某一天
     *
     * @return
     */
    public LocalDateTime previousOrSameWeekDay(DayOfWeek dayOfWeek) {
        return now().with(TemporalAdjusters.previousOrSame(dayOfWeek));
    }

    /**
     * 当前日期 改变年
     *
     * @param year
     * @return
     */
    public LocalDateTime withYear(int year) {
        return now().withYear(year);
    }

    /**
     * 当前日期 改变月
     *
     * @param month
     * @return
     */
    public LocalDateTime withMonth(int month) {
        return now().withMonth(month);
    }

    /**
     * 当前日期 改变小时
     *
     * @param hour
     * @return
     */
    public LocalDateTime withHour(int hour) {
        return now().withHour(hour);
    }

    /**
     * 当前日期 改变分钟
     *
     * @param minute
     * @return
     */
    public LocalDateTime withMinute(int minute) {
        return now().withMinute(minute);
    }

    /**
     * 当前日期 改变秒
     *
     * @param second
     * @return
     */
    public LocalDateTime withSecond(int second) {
        return now().withSecond(second);
    }

    /**
     * 当前日期所属年份,偏移月份
     *
     * @param dayOfMonth
     * @return
     */
    public static LocalDateTime withDayOfMonth(int dayOfMonth) {
        return now().withDayOfMonth(dayOfMonth);
    }

    /**
     * 当前日期所属年份,偏移天数
     *
     * @param dayOfYear
     * @return
     */
    public static LocalDateTime withDayOfYear(int dayOfYear) {
        return now().withDayOfYear(dayOfYear);
    }

    /**
     * 当前日期所属年份,偏移秒
     *
     * @param nanoOfSecond
     * @return
     */
    public static LocalDateTime withNanoOfSecond(int nanoOfSecond) {
        return now().withNano(nanoOfSecond);
    }

    //第七部分：时间段

    /**
     * 当天日期间隔 天数
     *
     * @param date 日期
     * @return 间隔 天数
     */
    public static long daysBetween(LocalDateTime date) {
        return now().toLocalDate().toEpochDay() - date.toLocalDate().toEpochDay();
    }


    /**
     * 两个日期间隔 天数
     *
     * @param date1 日期1
     * @param date2 日期2
     * @return
     */
    public static long daysBetween(LocalDateTime date1, LocalDateTime date2) {
        // return date1.toLocalDate().toEpochDay() - date2.toLocalDate().toEpochDay();
        return Duration.between(date1.toInstant(zoneOffset), date2.toInstant(zoneOffset)).toDays();
    }

    /**
     * 两个日期间隔 天数
     *
     * @param date1 日期1
     * @param date2 日期2
     * @return
     */
    public static long daysBetween(Date date1, Date date2) {
        return Math.abs(Duration.between(date1.toInstant(), date2.toInstant()).toDays());
    }


    /**
     * 两个日期间隔 年月日
     *
     * @param start 日期1
     * @param end 日期2
     * @return
     */
    public static String daysBetweenFormat(Date start, Date end) {
        long days = DateUtil.between(start, end, DateUnit.DAY);
        long years = days / 365; // 获得相差的年份
        long months = (days % 365) / 30; // 获得相差的月份
        long remainingDays = (days % 365) % 30;
        return years + "年" + months + "月" + remainingDays + "日";
    }


    public static void main(String[] args) {
        Date date = DateUtils.parseDate("2021-07-15");
        System.out.println(daysBetweenFormat(date,new Date()));


    }


    public static long secondsBetween(LocalDateTime date1, LocalDateTime date2) {
        return date1.toEpochSecond(zoneOffset) - date2.toEpochSecond(zoneOffset);
    }

    //第八部分：比较

    public static boolean isBefore(LocalDateTime date, LocalDateTime target) {
        return date.isBefore(target);
    }

    public static boolean isAfter(LocalDateTime date, LocalDateTime target) {
        return date.isAfter(target);
    }

    public static boolean isEqual(LocalDateTime date, LocalDateTime target) {
        return date.isEqual(target);
    }


    //第九部分：提取值
    public static long toEpochSecond() {
        return now().toEpochSecond(zoneOffset);
    }

    public static long toEpochMilli() {
        return now().toInstant(zoneOffset).toEpochMilli();
    }

    public static long getNano() {
        return now().toInstant(zoneOffset).getNano();
    }

    //第十部分：  解析格式化
    private static DateTimeFormatter findFormatter(String pattern) {
        //缓存优化
        DateTimeFormatter format = FORMATTERS.get(pattern);
        if (format == null) {
            format = DateTimeFormatter.ofPattern(pattern);
            FORMATTERS.put(pattern, format);
        }
        return format;
    }
}

